package cn.ywang.spring.customized.framework.context;

import cn.ywang.spring.customized.framework.annotation.Autowired;
import cn.ywang.spring.customized.framework.annotation.Controller;
import cn.ywang.spring.customized.framework.annotation.Service;
import cn.ywang.spring.customized.framework.aop.AopConfig;
import cn.ywang.spring.customized.framework.beans.BeanDefinition;
import cn.ywang.spring.customized.framework.beans.BeanPostProcessor;
import cn.ywang.spring.customized.framework.beans.BeanWrapper;
import cn.ywang.spring.customized.framework.context.support.XmlBeanDefinitionReader;
import cn.ywang.spring.customized.framework.core.BeanFactory;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author BeatificWang
 */
public class ApplicationContext extends DefaultListableBeanFactory implements BeanFactory {

	/**
	 * 待扫描类的位置
	 */
	private String[] configLocations;

	/**
	 * xml阅读器
	 */
	private XmlBeanDefinitionReader reader;

	/**
	 * 保存注册式单例信息
	 */
	private Map<String, Object> beanCacheMap = new HashMap<>();

	/**
	 * 用来存储所有被代理过的bean
	 */
	private Map<String, BeanWrapper> beanWrapperMap = new HashMap<>();

	public ApplicationContext(String... configLocations) {
		this.configLocations = configLocations;
		this.refresh();
	}

	/**
	 * 启动容器
	 */
	public void refresh() {
		// 定位
		this.reader = new XmlBeanDefinitionReader(this.configLocations);
		// 加载
		List<String> beanDefinitions = this.reader.loadBeanDefinitions();
		// 注册
		this.doRegistry(beanDefinitions);
		// 依赖注入（lazy-init=false）
		// 自动调用getBean进行依赖注入
		this.doAutowired();

		System.out.println("***************");
	}

	/**
	 * 开始执行自动化的依赖注入
	 */
	private void doAutowired() {
		// 注册式单例Bean
		for (Map.Entry<String, BeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
			String beanName = entry.getKey();
			BeanDefinition beanDefinition = entry.getValue();
			// 触发自动注入
			if (!beanDefinition.isLazyInit()) {
				Object object = this.getBean(beanName);
			}
		}
		// 被代理Bean
		for (Map.Entry<String, BeanWrapper> entry : this.beanWrapperMap.entrySet()) {
			String beanName = entry.getKey();
			BeanWrapper beanWrapper = entry.getValue();
			this.populateBean(beanName, beanWrapper.getOriginalInstance());
		}

	}

	/**
	 * 依赖注入
	 *
	 * @param beanName
	 * @param instance
	 */
	public void populateBean(String beanName, Object instance) {
		Class<?> clazz = instance.getClass();
		if (!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))) {
			return;
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {
				continue;
			}
			Autowired autowired = field.getAnnotation(Autowired.class);
			String autowiredBeanName = autowired.value().trim();
			if (StringUtils.isBlank(autowiredBeanName)) {
				autowiredBeanName = field.getType().getName();
			}
			field.setAccessible(true);
			try {
				field.set(instance, this.beanWrapperMap.get(autowiredBeanName).getWrapperInstance());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 真正将beanDefinition注册到beanDefinitionMap,容器初始化完毕
	 *
	 * @param beanDefinitions
	 */
	private void doRegistry(List<String> beanDefinitions) {
		for (String className : beanDefinitions) {
			// beanName有三种情况：
			// 1. 默认是类名--首字母小写
			// 2. 自定义
			// 3. 接口注入
			Class<?> clazz;
			try {
				clazz = Class.forName(className);
				// 如果是接口
				// 用它的实现类实例化
				if (clazz.isInterface()) {
					continue;
				}
				BeanDefinition beanDefinition = this.reader.registerBeanDefinitions(className);
				if (beanDefinition != null) {
					this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
				}
				Class<?>[] interfaces = clazz.getInterfaces();
				for (Class<?> i : interfaces) {
					// 如果多个实现类，只能覆盖，spring没那么智能，若不指定自定义名称，则spring会报错
					this.beanDefinitionMap.put(i.getName(), beanDefinition);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
	}

	/**
	 * 依赖注入从这里开始，读取BeanDefinition中信息，通过反射机制创建一个实例并进行返回
	 * 但是spring不会把原始信息释放出来，而是使用BeanWrapper进行封装,使用了装饰器模式
	 * 装饰器模式：
	 * 1. 保留原来的OOP关系
	 * 2. 需要进行扩展（为了以后的AOP扩展）
	 *
	 * @param beanName
	 * @return
	 */
	@Override
	public Object getBean(String beanName) {
		BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
		// 取出类名
		String className = beanDefinition.getBeanClassName();
		try {
			// 生成通知事件
			BeanPostProcessor beanPostProcessor = new BeanPostProcessor();
			// 生成实例对象
			Object instance = this.instantiateBean(beanDefinition);
			if (instance == null) {
				return null;
			}
			// 在实例初始化之前调用一次
			beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
			BeanWrapper beanWrapper = new BeanWrapper(instance);
			beanWrapper.setAopConfig(this.instantiateAopConfig(beanDefinition));
			beanWrapper.setBeanPostProcessor(beanPostProcessor);
			this.beanWrapperMap.put(beanName, beanWrapper);
			// 在实例初始化以后调用一次
			beanPostProcessor.postProcessAfterInitialization(instance, beanName);
			// 相当于保留了可以进行自定义操作的空间
			return this.beanWrapperMap.get(beanName).getWrapperInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 初始化AopConfig
	 *
	 * @param beanDefinition
	 * @return
	 */
	private AopConfig instantiateAopConfig(BeanDefinition beanDefinition) {
		AopConfig config = new AopConfig();
		// 切点规则
		String expression = this.reader.getContextConfig().getProperty("pointCut");
		//提取方法
		String[] before = this.reader.getContextConfig().getProperty("aspectBefore").split("\\s");
		String[] after = this.reader.getContextConfig().getProperty("aspectAfter").split("\\s");
		String className = beanDefinition.getBeanClassName();
		try {
			Pattern pattern = Pattern.compile(expression);
			// 切面类
			Class<?> aspectClass = Class.forName(before[0]);
			// 该bean类也可以当成另外一个切面
			Class<?> clazz = Class.forName(className);
			// bean的原生方法
			for (Method method : clazz.getMethods()) {
				Matcher matcher = pattern.matcher(method.toString());
				if (matcher.matches()) {
					// 满足切面规则的类的方法，加载到配置中
					config.put(method, aspectClass.newInstance(), new Method[]{aspectClass.getMethod(before[1]), aspectClass.getMethod(after[1])});
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return config;
	}


	protected void loadBeanDefinitions(BeanFactory beanFactory) {
	}

	/**
	 * 传一个BeanDefinition，返回一个实例Bean
	 *
	 * @param beanDefinition
	 * @return
	 */
	private Object instantiateBean(BeanDefinition beanDefinition) {
		Object instance = null;
		String className = beanDefinition.getBeanClassName();
		try {
			// 因为根据Class才能确定一个类是否有实例
			if (!this.beanCacheMap.containsKey(className)) {
				Class<?> clazz = Class.forName(className);
				instance = clazz.newInstance();
				this.beanCacheMap.put(className, instance);
			}
			else {
				instance = this.beanCacheMap.get(className);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return instance;
	}


	public String[] getBeanNames() {
		return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
	}

	public Properties getConfig() {
		return this.reader.getContextConfig();
	}

}
